﻿// -----------------------------
// Copyright © Kodi Studios 2012
// -----------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Threading.Tasks;
using System.Reflection;
using System.Windows.Input;

namespace Kodi.Picturethrill
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Private Fields

        private Thread backgroundDownloadThread;
        private AdvancedSettings advancedSettings;
        private bool quietMode;
        private bool networkRequiredMode;
        private bool helpMode;
        internal bool uninstallMode;
        internal bool installMode;
        private string directory = string.Empty;
        private Settings settings = new Settings();
        private List<IImageProvider> imageProviders = new List<IImageProvider>();

        // Key is site Url
        private Dictionary<string, IImageProvider> imageProvidersHash = new Dictionary<string, IImageProvider>();
        private Dictionary<string, ImagePreviewControl> imagePreviewControlsHash = new Dictionary<string, ImagePreviewControl>();

        private CacheServiceWrapper cacheServiceWrapper = new CacheServiceWrapper();
        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor.
        /// </summary>
        public MainWindow()
        {
            using (Log.Group())
            {
                try
                {
                    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

                    // Prepare CommandLine Arguments
                    List<string> args = new List<string>();
                    if (Application.Current.Properties[App.ArgsPropertiesName] != null)
                    {
                        args.AddRange((string[])Application.Current.Properties[App.ArgsPropertiesName]);
                    }

                    Log.Comment("Process: " + Process.GetCurrentProcess().MainModule.FileName);

                    // Log CommandLine Arguments
                    string allArgs = string.Empty;
                    foreach (string arg in args)
                    {
                        allArgs += arg + " ";
                    }

                    Log.Comment("CommandLine Arguments: " + allArgs);

                    FileVersionInfo picturethrillFileVersionInfo = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
                    Version currentPicturethrillVersion = new Version(picturethrillFileVersionInfo.ProductVersion);
                    Log.Comment("Picturethrill Version: " + currentPicturethrillVersion.ToString());
                    Log.Comment("OS Version: " + Environment.OSVersion.Version.ToString());
                    Log.Comment("OS Name: " + Environment.OSVersion.ToString());

                    bool attachMode = false;

                    // Parse CommandLine Arguments
                    List<CommandLineArgumentFlag> flags = new List<CommandLineArgumentFlag>();
                    flags.Add(new CommandLineArgumentFlag("d", (s) => { this.directory = s; }));
                    flags.Add(new CommandLineArgumentFlag("q", () => { this.quietMode = true; }));
                    flags.Add(new CommandLineArgumentFlag("n", () => { this.networkRequiredMode = true; }));
                    flags.Add(new CommandLineArgumentFlag("i", () => { this.installMode = true; }));
                    flags.Add(new CommandLineArgumentFlag("u", () => { this.uninstallMode = true; }));
                    flags.Add(new CommandLineArgumentFlag("a", () => { attachMode = true; }));
                    flags.Add(new CommandLineArgumentFlag("h", () => { this.helpMode = true; }));
                    flags.Add(new CommandLineArgumentFlag("?", () => { this.helpMode = true; }));
                    CommandLineArgumentParser.Parse(args, flags);

                    if (attachMode)
                    {
                        // Ability to attach debugger is Pri1
                        MessageBox.Show("Paused to allow attaching a debugger. "
                            + "Running process " + Process.GetCurrentProcess().ProcessName + "(" + Process.GetCurrentProcess().Id + ")");
                    }

                    InitializeComponent();

                    this.Loaded += new RoutedEventHandler(this.OnMainContainer_Loaded);

                    // Create AdvancedWindow
                    this.advancedSettings = new AdvancedSettings();

                    this.settings.Load();
                    this.ApplySettings();

                    // Set WindowState of MainWindow
                    if (this.installMode || this.uninstallMode || this.quietMode)
                    {
                        this.WindowState = System.Windows.WindowState.Minimized;
                    }
                }
                catch (Exception e)
                {
                    // Catch all exceptions
                    Log.Comment(e.ToString());
                }
            }
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs eventArgs)
        {
            Exception e = eventArgs.ExceptionObject as Exception;
            if (e != null)
            {
                Log.Comment("AppDomain Unhandled Exception: " + e.ToString());
            }
            else
            {
                Log.Comment("AppDomain Unhandled Exception: Unknown Exception Type");
            }

            Log.Comment("Picturethrill is Terminating");
        }

        /// <summary>
        /// Removes all Picturethrill tasks.
        /// </summary>
        private static void DeleteTasks()
        {
            using (Log.Group())
            {
                PicturethrillTask picturethrillTask = new PicturethrillTask();
                PicturethrillUpdateTask updateTask = new PicturethrillUpdateTask();

                // Unregister Mode
                if (picturethrillTask.TaskExists)
                {
                    picturethrillTask.DeleteTask();
                }

                // Unregister Mode
                if (updateTask.TaskExists)
                {
                    updateTask.DeleteTask();
                }
            }
        }

        /// <summary>
        /// Creates needed Picturethrill tasks.
        /// </summary>
        private void CreateTasks()
        {
            PicturethrillTask picturethrillTask = new PicturethrillTask();
            PicturethrillUpdateTask updateTask = new PicturethrillUpdateTask();

            // installMode or Normal Mode
            if (this.advancedSettings.AutomaticUpdate || this.advancedSettings.SaveNewPictures)
            {
                // Check if task already exists.
                if (!picturethrillTask.TaskExists)
                {
                    // Will override task with different path.
                    picturethrillTask.CreateTask();
                }
            }
            else
            {
                if (picturethrillTask.TaskExists)
                {
                    picturethrillTask.DeleteTask();
                }
            }

            if (this.advancedSettings.AutomaticUpdate)
            {
                // Check if task already exists.
                if (!updateTask.TaskExists)
                {
                    // Will override task with different path.
                    updateTask.CreateTask();
                }
            }
            else
            {
                if (updateTask.TaskExists)
                {
                    updateTask.DeleteTask();
                }
            }
        }

        /// <summary>
        /// Applies saved Settings.
        /// </summary>
        private void ApplySettings()
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                this.advancedSettings.SavedImagesDirectory = this.settings.ImageDirectory;
                this.advancedSettings.UpdateDesktopBackground = this.settings.UpdateDesktopBackground;
                this.advancedSettings.SaveNewPictures = this.settings.SaveNewPictures;
                this.advancedSettings.AutomaticUpdate = this.settings.AutomaticUpdate;

                using (Log.Group("Advanced Settings"))
                {
                    Log.Comment("ImageDirectory: " + this.advancedSettings.SavedImagesDirectory);
                    Log.Comment("UpdateDesktopBackground: " + this.advancedSettings.UpdateDesktopBackground);
                    Log.Comment("SaveNewPictures: " + this.advancedSettings.SaveNewPictures);
                    Log.Comment("AutomaticUpdate: " + this.advancedSettings.AutomaticUpdate);
                }
            }
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// Clean up when Picturethrill is closed.
        /// </summary>
        /// <param name="eventArgs">Event arguments.</param>
        protected override void OnClosed(EventArgs eventArgs)
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                // Save General Settings
                this.settings.ImageDirectory = this.advancedSettings.SavedImagesDirectory;
                this.settings.UpdateDesktopBackground = this.advancedSettings.UpdateDesktopBackground;
                this.settings.SaveNewPictures = this.advancedSettings.SaveNewPictures;
                this.settings.AutomaticUpdate = this.advancedSettings.AutomaticUpdate;

                this.settings.SelectedUrls.Clear();
                foreach (ImagePreviewControl imagePreviewControl in this.supportedWebsitesListBox.Items)
                {
                    if (imagePreviewControl.IsChecked)
                    {
                        this.settings.SelectedUrls.Add(imagePreviewControl.Url);
                    }
                }
                this.settings.Save();

                using (Log.Group("Remove unused Temp Bmp files"))
                {
                    string currentWallpaperBmp = DesktopBackground.GetWallpaperBmp();
                    foreach (string wallpaperBmp in Directory.GetFiles(Settings.WallpaperBmpsPath, "*", SearchOption.AllDirectories))
                    {
                        if (!string.Equals(currentWallpaperBmp, wallpaperBmp, StringComparison.OrdinalIgnoreCase))
                        {
                            Log.Comment("Deleting file: " + wallpaperBmp);
                            File.Delete(wallpaperBmp);
                        }
                        else
                        {
                            Log.Comment("Not Deleting file: " + wallpaperBmp);
                        }
                    }
                }

                // Save Settings for Cache Service
                this.cacheServiceWrapper.Save();
                this.cacheServiceWrapper.RemoveNonCached();

                base.OnClosed(eventArgs);
            }
        }
        #endregion

        #region Event Methods

        /// <summary>
        /// Process Command-Line arguments.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="eventArgs">Event arguments.</param>
        private void OnMainContainer_Loaded(object sender, RoutedEventArgs eventArgs)
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                // Order of these statements is important!!!
                if (helpMode)
                {
                    MessageBox.Show("Usage:\n"
                        + Process.GetCurrentProcess().ProcessName + ".exe [Options]\n"
                        + "\n"
                        + "Options:\n"
                        + "-?\t\tDisplay this help message\n"
                        + "-d\t\tDownload images automatically upon program start\n"
                        + "-a\t\tPause upon program start to allow attaching a debugger\n"
                        + "-f FolderName\tSpecify directory where to place downloaded images\n"
                        + "-q\t\tQuiet mode, no UI\n"
                        );
                    this.Close();
                    return;
                }

                // Set Save Directory, if any
                if (!string.IsNullOrEmpty(directory))
                {
                    this.advancedSettings.SavedImagesDirectory = directory;
                }

                if (this.uninstallMode)
                {
                    KillRogueProcesses();
                    DeleteTasks();
                    this.Close();
                    return;
                }

                this.CreateTasks();

                if (this.installMode)
                {
                    this.Close();
                    return;
                }

                if (this.networkRequiredMode)
                {
                    bool internetConnectionAvailable = false;
                    DateTime timeoutTime = DateTime.Now.AddMilliseconds(2 * 60 * 1000);
                    do
                    {
                        if (!NetworkUtilities.InternetConnectionAvailable())
                        {
                            Log.Comment("Network connection unavailable. Retry after 10 seconds.");
                            Thread.Sleep(TimeSpan.FromSeconds(10));
                        }
                        else
                        {
                            internetConnectionAvailable = true;
                            break;
                        }
                    } while (timeoutTime > DateTime.Now);

                    if (internetConnectionAvailable)
                    {
                        Log.Comment("Network connection available.");
                    }
                    else
                    {
                        Log.Comment("Network connection unavailable. Network connection timed out after 2 minutes. Stop execution.");
                        this.Close();
                        return;
                    }
                }

                this.InitializeProviders();

                if (quietMode)
                {
                    this.OnQuietMode();
                    this.Close();
                    return;
                }

                this.StartWallpaperPreviewThread();
            }
        }

        private static void KillRogueProcesses()
        {
            using (Log.Group())
            {
                // Find all Picturethrill processes besides current one
                Process currentProcess = Process.GetCurrentProcess();
                Process[] picturethrillProcesses = Process.GetProcessesByName(currentProcess.ProcessName);
                if (picturethrillProcesses.Length == 1)
                {
                    Log.Comment("No rogue Picturethrill processes detected");
                }
                foreach (Process picturethrillProcess in picturethrillProcesses)
                {
                    try
                    {
                        if (picturethrillProcess.Id != currentProcess.Id)
                        {
                            Log.Comment("Kill rogue Picturethrill(" + picturethrillProcess.Id + ")");
                            picturethrillProcess.Kill();
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Comment(e.ToString());
                    }
                }
            }
        }

        private void InitializeProviders()
        {
            using (Log.Group())
            {
                // Create Providers
                // TODO: this should ideally come from config file :)
                this.imageProviders.Add(new BingImageProvider());
                this.imageProviders.Add(new NasaImageProvider());
                this.imageProviders.Add(new EarthScienceImageProvider());
                this.imageProviders.Add(new NationalGeographicImageProvider());
                this.imageProviders.Add(new NatureImageProvider());

                // Create Contol for each Provider
                foreach (IImageProvider imageProvider in this.imageProviders)
                {
                    this.imageProvidersHash.Add(imageProvider.SiteName, imageProvider);

                    ImagePreviewControl imagePreviewControl = new ImagePreviewControl(this, imageProvider);
                    imagePreviewControl.PreviewImageUpdated += this.OnImagePreviewControl_PreviewImageUpdated;
                    this.supportedWebsitesListBox.Items.Add(imagePreviewControl);

                    this.imagePreviewControlsHash.Add(imageProvider.SiteName, imagePreviewControl);
                }

                this.cacheServiceWrapper.Load();

                // Register with "Checked" for RadioButton Logic control.
                foreach (ImagePreviewControl imagePreviewControl in this.supportedWebsitesListBox.Items)
                {
                    imagePreviewControl.Checked += new RoutedEventHandler(this.OnImagePreviewControl_Checked);
                }

                // Select active ImageProvider
                if (this.settings.Loaded && this.settings.SelectedUrls.Count > 0)
                {
                    foreach (ImagePreviewControl imagePreviewControl in this.supportedWebsitesListBox.Items)
                    {
                        if (string.Equals(this.settings.SelectedUrls[0], imagePreviewControl.Url, StringComparison.OrdinalIgnoreCase))
                        {
                            imagePreviewControl.IsChecked = true;
                        }
                    }
                }
                else
                {
                    // No settings, select first ImageProvider
                    if (this.supportedWebsitesListBox.Items.Count > 0)
                    {
                        this.supportedWebsitesListBox.SelectedItem = this.supportedWebsitesListBox.Items[0];
                    }
                }

                // TODO: it would be nice to move this to "OnClosed", however
                // now, deleting fails because current process holds handle to image files
                // inside Preview folder.
                // TODO: Cache may not work really well with multiple instances of Picturethrill.exe
                string wallpapersPreviewDirectoryName = Settings.WallpaperPreviewsPath;
                if (Directory.Exists(wallpapersPreviewDirectoryName))
                {
                    try
                    {
                        Log.Comment("Delete WallpaperPreviews Directory: " + wallpapersPreviewDirectoryName);
                        Directory.Delete(wallpapersPreviewDirectoryName, recursive: true);
                    }
                    catch (Exception e)
                    {
                        Log.Comment(e.ToString());
                    }
                }

                // TODO: it would be nice to move this to "OnClosed", however
                // now, deleting fails because current process holds handle to image files
                // inside Preview folder.
                // This should get executed during uninstall, but doesn't
                string wallpapersDownloadsDirectoryName = Settings.WallpaperDownloadsPath;
                if (Directory.Exists(wallpapersDownloadsDirectoryName))
                {
                    try
                    {
                        Log.Comment("Delete WallpaperDownloads Directory: " + wallpapersDownloadsDirectoryName);
                        Directory.Delete(wallpapersDownloadsDirectoryName, recursive: true);
                    }
                    catch (Exception e)
                    {
                        Log.Comment(e.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// Sets selected image as Desktop Background.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="eventArgs">Event arguments.</param>
        private void OnWallpaperButton_Click(object sender, RoutedEventArgs eventArgs)
        {
            using (Log.Group())
            {
                if (sourceImagePreviewControlSelected != null)
                {
                    this.SetWallpaperAndSave(sourceImagePreviewControlSelected.ImageProvider);
                }
            }
        }

        /// <summary>
        /// Displays Advanced Dialog.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="eventArgs">Event arguments.</param>
        private void OnAdvancedButton_Click(object sender, RoutedEventArgs eventArgs)
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                AdvancedWindow advancedWindow = new AdvancedWindow();

                advancedWindow.AutomaticUpdate = advancedSettings.AutomaticUpdate;
                advancedWindow.ImageDirectory = advancedSettings.SavedImagesDirectory;
                advancedWindow.SaveNewPictures = advancedSettings.SaveNewPictures;
                advancedWindow.UpdateDesktopBackground = advancedSettings.UpdateDesktopBackground;

                advancedWindow.ShowDialog();

                this.advancedSettings.AutomaticUpdate = advancedWindow.AutomaticUpdate;
                this.advancedSettings.SavedImagesDirectory = advancedWindow.ImageDirectory;
                this.advancedSettings.SaveNewPictures = advancedWindow.SaveNewPictures;
                this.advancedSettings.UpdateDesktopBackground = advancedWindow.UpdateDesktopBackground;
            }
        }

        private void OnSupportedWebsitesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            using (Log.Group())
            {
                if (e.AddedItems.Count > 0)
                {
                    this.wallpaperButton.IsEnabled = false;

                    ImagePreviewControl imagePreviewControl = e.AddedItems[0] as ImagePreviewControl;
                    if (imagePreviewControl != null)
                    {
                        imagePreviewControl.IsChecked = true;

                        // Dehighlight Selected Item (Deselect it, so that only RadioButton gets checked)
                        this.supportedWebsitesListBox.SelectedItems.Clear();

                        if (!string.IsNullOrEmpty(imagePreviewControl.ImagePath))
                        {
                            // Enable "Wallpaper" button
                            this.wallpaperButton.IsEnabled = true;
                        }
                    }
                }
            }
        }

        private void OnImagePreviewControl_PreviewImageUpdated(ImagePreviewControl imagePreviewControl)
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                this.Dispatcher.Invoke((ThreadStart)delegate
                {
                    if (imagePreviewControl.IsChecked)
                    {
                        // TODO: Need to fix this with install/uninstall
                        this.wallpaperButton.IsEnabled = true;
                    }
                });
            }
        }

        /// <summary>
        /// Serves as a Logic controller for Radio Buttons.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="eventArgs">Event arguments.</param>
        private void OnImagePreviewControl_Checked(object sender, RoutedEventArgs eventArgs)
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                // Deselect all other Radio Buttons.
                ImagePreviewControl sourceImagePreviewControl = sender as ImagePreviewControl;
                if (sourceImagePreviewControl != null && sourceImagePreviewControl.IsChecked)
                {
                    foreach (ImagePreviewControl imagePreviewControl in this.supportedWebsitesListBox.Items)
                    {
                        if (!object.ReferenceEquals(sourceImagePreviewControl, imagePreviewControl))
                        {
                            imagePreviewControl.IsChecked = false;
                        }
                    }
                    this.sourceImagePreviewControlSelected = sourceImagePreviewControl;
                }
            }
        }

        private ImagePreviewControl sourceImagePreviewControlSelected;

        /// <summary>
        /// Opens default browser and navigates to Picturethrill facebook page.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Event arguments.</param>
        private void OnFacebookButton_Click(object sender, RoutedEventArgs e)
        {
            using (Log.Group())
            {
                Process.Start("https://www.facebook.com/picturethrill.en");
            }
        }

        /// <summary>
        /// Opens default browser and navigates to Kodi Studios Paypal page.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Event arguments.</param>
        private void OnDonateButton_Click(object sender, RoutedEventArgs e)
        {
            //https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RBVTSADM2FD34
            using (Log.Group())
            {
                Process.Start("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RBVTSADM2FD34");
            }
        }

        #endregion

        #region Helper Methods

        private void StartWallpaperPreviewThread()
        {
            this.backgroundDownloadThread = new Thread(this.DownloadWallpaperPreviews);
            this.backgroundDownloadThread.Start();
        }

        // key: image url
        // value: local path to image
        private Dictionary<string, string> downloadedImagesCached = new Dictionary<string, string>();
        private object downloadedImagesCachedLock = new object();

        public void SetWallpaper(IImageProvider imageProvider)
        {
            using (Log.Group())
            {
                IList<string> downloadedImagesList = this.DownloadImages(imageProvider, Settings.WallpaperDownloadsPath);
                if (downloadedImagesList.Count > 0)
                {
                    DesktopBackground.SetWallpaper(downloadedImagesList[0]);
                }
            }
        }

        public void SetWallpaperAndSave(IImageProvider imageProvider)
        {
            using (Log.Group())
            {
                this.SetWallpaper(imageProvider);

                if (this.advancedSettings.SaveNewPictures)
                {
                    this.DownloadImages(imageProvider, this.advancedSettings.SavedImagesDirectory);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void OnQuietMode()
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name))
            {
                // Update Desktop Background
                bool updateDesktopBackgroundAllowed = this.advancedSettings.UpdateDesktopBackground;
                if (updateDesktopBackgroundAllowed)
                {
                    if (this.sourceImagePreviewControlSelected != null)
                    {
                        this.SetWallpaper(this.sourceImagePreviewControlSelected.ImageProvider);
                    }
                }

                // Save New Pictures
                bool saveNewPicturesAllowed = this.advancedSettings.SaveNewPictures;
                if (saveNewPicturesAllowed)
                {
                    if (this.sourceImagePreviewControlSelected != null)
                    {
                        this.DownloadImages(this.sourceImagePreviewControlSelected.ImageProvider, this.advancedSettings.SavedImagesDirectory);
                    }
                }
            }
        }

        private string SaveImage(string directoryPath, string imageProviderName, string imageUrl)
        {
            using (Log.Group(MethodInfo.GetCurrentMethod().Name + " " + directoryPath + " " + imageProviderName + " " + imageUrl))
            {
                string providerDirectoryPath = directoryPath;
                if (!Directory.Exists(providerDirectoryPath))
                {
                    Directory.CreateDirectory(providerDirectoryPath);
                }

                string imageFileName = string.Empty;
                string imageExtension = Path.GetExtension(imageUrl);
                if (string.IsNullOrEmpty(imageExtension))
                {
                    imageExtension = ".jpg"; // default extension is jpg
                }

                string imageUrlClean = Path.GetFileNameWithoutExtension(imageUrl);

                string invalidChars = new string(Path.GetInvalidFileNameChars());
                foreach (char invalidChar in invalidChars)
                {
                    imageUrlClean = imageUrlClean.Replace(invalidChar.ToString(), "");
                }

                string randomFileName = imageUrlClean;

                string providerPrefix = imageProviderName + "_";
                if (randomFileName.StartsWith(providerPrefix, StringComparison.OrdinalIgnoreCase))
                {
                    providerPrefix = string.Empty;
                }

                // imageFileName: Nasa_selrijfsdi.jpg
                imageFileName = Path.Combine(providerDirectoryPath, providerPrefix + randomFileName + imageExtension);

                if (File.Exists(imageFileName))
                {
                    return imageFileName;
                }

                try
                {
                    string webClientImageUrl = imageUrl;
                    lock (this.downloadedImagesCachedLock)
                    {
                        if (this.downloadedImagesCached.ContainsKey(imageUrl))
                        {
                            webClientImageUrl = this.downloadedImagesCached[imageUrl];
                        }
                    }

                    using (WebClient webClient = new WebClient())
                    {
                        // Works with both internet urls and local filenames
                        webClient.DownloadFile(webClientImageUrl, imageFileName);
                    }

                    lock (this.downloadedImagesCachedLock)
                    {
                        if (!this.downloadedImagesCached.ContainsKey(imageUrl))
                        {
                            this.downloadedImagesCached[imageUrl] = imageFileName;
                        }
                    }
                }
                catch (Exception e)
                {
                    Log.Comment("Unable to download image from provider '" + imageProviderName
                        + "'. Image URL: '" + imageUrl
                        + "'. To directory '" + directoryPath + "'. "
                        + e.ToString());
                }

                if (File.Exists(imageFileName))
                {
                    return imageFileName;
                }
                else
                {
                    return string.Empty;
                }
            }
        }

        /// <summary>
        /// Saves images from currently selected providers
        /// to specified directory.
        /// </summary>
        /// <param name="directoryPath">Directory to save images to.</param>
        /// <remarks>This is a long operation, perform on non UI thread.</remarks>
        private IList<string> DownloadImages(IImageProvider imageProvider, string directoryPath)
        {
            using (Log.Group())
            {
                List<IImageProvider> selectedImageProviders = new List<IImageProvider>();

                List<string> savedPictureFilePaths = new List<string>();

                foreach (string imageProviderUrl in imageProvider.ImageUrls)
                {
                    string imageUrl = imageProviderUrl;

                    if (string.IsNullOrEmpty(imageUrl))
                    {
                        // Use cached
                        imageUrl = this.cacheServiceWrapper.GetCached(imageProvider.Name);
                        if (string.IsNullOrEmpty(imageUrl))
                        {
                            continue;
                        }
                    }

                    string savedPictureFilePath = this.SaveImage(directoryPath, imageProvider.Name, imageUrl);
                    savedPictureFilePaths.Add(savedPictureFilePath);
                }

                return savedPictureFilePaths;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <remarks>Thread-safe.</remarks>
        private void DownloadWallpaperPreviews()
        {
            using (Log.Group())
            {
                Parallel.ForEach<IImageProvider>(this.imageProviders, (imageProvider) =>
                {
                    string imageUrl = imageProvider.ImageUrls[0];

                    if (string.IsNullOrEmpty(imageUrl))
                    {
                        Log.Comment("Provider " + imageProvider.Name + " has no url. Using Cached.");
                        imageUrl = cacheServiceWrapper.GetCached(imageProvider.Name);
                    }

                    Log.Comment("Provider " + imageProvider.Name + " image url: " + imageUrl);

                    if (!string.IsNullOrEmpty(imageUrl))
                    {
                        string filePath = this.SaveImage(Settings.WallpaperPreviewsPath, imageProvider.Name, imageUrl);

                        if (string.IsNullOrEmpty(filePath))
                        {
                            Log.Comment("Provider " + imageProvider.Name + " has no image at url " + imageUrl + ". Using Cached.");
                            filePath = cacheServiceWrapper.GetCached(imageProvider.Name);
                        }

                        Log.Comment("Provider " + imageProvider.Name + " image file: " + filePath);

                        if (!string.IsNullOrEmpty(filePath))
                        {
                            this.imagePreviewControlsHash[imageProvider.SiteName].ImagePath = filePath;
                            Log.Comment("Provider " + imageProvider.Name + " updated Preview image");

                            this.cacheServiceWrapper.Cache(imageProvider.Name, filePath);
                            Log.Comment("Provider " + imageProvider.Name + " image cached at: " + this.cacheServiceWrapper.GetCached(imageProvider.Name));
                        }
                    }
                    else
                    {
                        Log.Comment("Provider " + imageProvider.Name + " imageUrl is null or empty");
                    }
                });
            }
        }

        #endregion

        // there are needed to change cursor on donate and fb from arrow to hand.
        Cursor previousCursor = Cursors.Arrow;
        private void OnImageButton_MouseEnter(object sender, MouseEventArgs e)
        {
            if (this.Cursor != Cursors.Wait)
            {
                previousCursor = this.Cursor;
                this.Cursor = Cursors.Hand;
            }
        }

        private void OnImageButton_MouseLeave(object sender, MouseEventArgs e)
        {
            if (this.Cursor == Cursors.Hand)
            {
                this.Cursor = previousCursor;
            }
        }
    }
}
